﻿
<!--
https://github.com/netnr
https://gitee.com/netnr
https://www.netnr.com
https://zme.ink
-->


<!DOCTYPE html>
<html>
<head>

    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <link rel='shortcut icon' href='/favicon.ico' type='image/x-icon' />


    <title>PDM查看 脚本服务</title>

    <meta name="keywords" content="netnr NET牛人 PDM查看" />
    <meta name="description" content="Script Services PowerDesigner解析、查看" />

</head>
<body>
<div id='LoadingMask' style='position:fixed;top:0;left:0;bottom:0;right:0;background-color:white;z-index:19999;background-image:url("/images/loading.svg");background-repeat:no-repeat;background-position:48% 45%'></div>
<link href='https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/css/bootstrap.min.css' rel='stylesheet' />
<link href='https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css' rel='stylesheet' />
<script src='https://cdn.jsdelivr.net/npm/jquery@3.5.0/dist/jquery.min.js'></script>
<script src='https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/js/bootstrap.bundle.min.js'></script>
<script src='https://cdn.jsdelivr.net/npm/jzjs@2.0.2/2.0.2/jz.min.js'></script>
<link href='https://cdn.jsdelivr.net/npm/netnrnav@1.1.1/src/netnrnav.css' rel='stylesheet' />
<script src='https://cdn.jsdelivr.net/npm/netnrnav@1.1.1/src/netnrnav.js' defer async></script>
        <link href="/css/global.css?v=ILaxEDuAKPcfx0Dw00WIDWl_jGo3BYpoTjyINR4ydjY" rel="stylesheet" />
        <script src="/js/global.js?v=pKcbh4RJesRNHWnZRek0eTm5akwKQWgf9Qa-pLuFFAs"></script>
            <!--导航-->
            <button type="button" class="MenuToggle show-in-mobile btn btn-lg btn-warning ml-3 mt-3">
                <span class="fa fa-align-justify"></span>
            </button>
            <div class="netnrnav horizontal">
                <div class="netnrnav-wrapper">
                    <ul>
                        <li><a href="https://www.netnr.com" class="siteIcon"><img src="/favicon.ico" /> 首页</a></li>
                        <li><a href="https://www.netnr.com/gist/discover" title="代码片段"><span class="fa fa-fw fa-file-code-o"></span> Gist</a></li>
                        <li><a href="https://www.netnr.com/run/discover" title="在线运行代码"><span class="fa fa-fw fa-play"></span> Run</a></li>
                        <li><a href="https://www.netnr.com/doc" title="文档管理"><span class="fa fa-fw fa-book"></span> Doc</a></li>
                        <li><a href="https://www.netnr.com/draw/discover" title="绘制"><span class="fa fa-fw fa-paint-brush"></span> Draw</a></li>
                        <li><a href="/" title="ss.js.org"><span class="fa fa-fw fa-wrench"></span> 服务</a></li>
                        <li style="background-image: radial-gradient(ellipse, white, rgb(227, 248, 240))">
                            <!--标题-->
                                <a href='javascript:location.reload(false)' title="PowerDesigner解析、查看">
                                    <svg class="titleicon"><use xlink:href="#pdm"></use></svg> PDM&#x67E5;&#x770B;
                                </a>
                        </li>
                        <li>
                            <a class="text-muted"><span class="fa fa-fw fa-ellipsis-h"></span></a>
                            <ul>
                                <li><a target="_blank" title="码云" href="https://gitee.com/netnr"><i class="fa fa-fw fa-git"></i> Gitee <sup><i class="fa small fa-external-link"></i></sup></a></li>
                                <li><a target="_blank" title="GitHub" href="https://github.com/netnr"><i class="fa fa-fw fa-github"></i> GitHub <sup><i class="fa small fa-external-link"></i></sup></a></li>
                            </ul>
                        </li>
                    </ul>
                </div>
            </div>
        <div class="min300 mt-3">
            <div class="container-fluid">
    <div class="row">
        <div class="col-md-12">
            <div class="input-group">
                <div class="custom-file">
                    <input id="txtFile" type="file" class="custom-file-input" multiple accept="*">
                    <label class="custom-file-label nrtip" dv="选择（拖拽）多个 PDM 文件">选择（拖拽）多个 PDM 文件</label>
                </div>
            </div>
            <div class="mt-3 grid ag-theme-alpine"></div>
        </div>
    </div>
</div>

<script src='https://cdn.jsdelivr.net/npm/ag-grid-enterprise@25.0.1/dist/ag-grid-enterprise.min.js'></script>


<script>

    var pdm = {
        dataArray: [],
        dataItem: function () {
            return {
                txt: null,
                xml: null,
                json: null
            }
        },
        grid: null,
        gbox: document.querySelector('.grid'),
        init: function () {

            ss.agg.lk();

            ss.receiveFiles(function (files) {
                pdm.tip("Loading ...");

                var arr = [];
                for (var i = 0; i < files.length; i++) {
                    var file = files[i];
                    if (file.name.split('.').pop().toLowerCase() == "pdm") {

                        var pi = new Promise(function (resolve, reject) {
                            var fr = new FileReader();
                            fr.onload = function (e) {
                                try {
                                    var di = pdm.parse(e.target.result);
                                    resolve(di);
                                } catch (e) {
                                    console.log(e)
                                    reject(e)
                                }
                            }
                            fr.readAsText(file);
                        });

                        arr.push(pi);
                    }
                }

                Promise.all(arr).then(res => {
                    pdm.dataArray = pdm.dataArray.concat(res);
                    pdm.view(pdm.gridData());

                    pdm.tip();
                })

                $("#txtFile").val('');
            }, "#txtFile");

            $(window).resize(pdm.resize).on('hashchange', function () {
                pdm.openUrl();
            });
            pdm.openUrl();
        },

        tip: function (msg) {
            if (msg != null) {
                $('.nrtip').html(msg);
            } else {
                $('.nrtip').html($('.nrtip').attr('dv'));
            }
        },

        gridData: function () {

            var data = [];
            //文件

            for (var d1 = 0; d1 < pdm.dataArray.length; d1++) {
                var id1 = pdm.dataArray[d1].json;

                var PowerDesignerTarget = id1.PowerDesigner.Target;
                var PowerDesignerVersion = id1.PowerDesigner.version;
                var ModelCode = id1.Model.Code;

                var Users = "Users";
                var Tables = "Tables";

                //表
                for (var d2 = 0; d2 < id1.Tables.length; d2++) {
                    var id2 = id1.Tables[d2];

                    var TableName = id2.Name;
                    var TableCode = id2.Code;
                    var TableComment = id2.Comment;

                    //列
                    for (var d3 = 0; d3 < id2.Columns.length; d3++) {
                        var id3 = id2.Columns[d3];

                        var ColumnName = id3.Name;
                        var ColumnCode = id3.Code;
                        var ColumnComment = id3.Comment;
                        var ColumnDataType = id3.DataType;
                        var ColumnLength = id3.Length;
                        var ColumnFieldOrder = id3.FieldOrder;
                        var ColumnPrimaryKey = id3.PrimaryKey;
                        var ColumnClusterObject = id3.ClusterObject;
                        var ColumnMandatory = id3.Mandatory;
                        var ColumnDefaultValue = id3.DefaultValue;

                        var obj = {
                            PowerDesignerTarget, PowerDesignerVersion, ModelCode,
                            Users, Tables,
                            TableName, TableCode, TableComment,
                            ColumnName, ColumnCode, ColumnComment, ColumnDataType, ColumnLength, ColumnFieldOrder, ColumnPrimaryKey, ColumnClusterObject, ColumnMandatory, ColumnDefaultValue
                        };

                        data.push(obj);
                    }
                }
            }

            return data;
        },

        view: function (data) {
            if (pdm.grid) {
                pdm.grid.gridOptions.api.setRowData(pdm.grid.gridOptions.rowData = pdm.gridData())
                return;
            }

            var gridOptions = {
                columnDefs: [
                    {
                        field: "ModelCode", headerName: "Model",
                        rowGroup: true, hide: true
                    },
                    {
                        field: "Users", headerName: "Users", hide: true
                    },
                    {
                        field: "Tables", headerName: "Tables", hide: true
                    },
                    {
                        field: "TableName", headerName: "Table Name",
                        rowGroup: true, hide: true
                    },
                    {
                        field: "TableCode", headerName: "Code", hide: true
                    },
                    {
                        field: "TableComment", headerName: "Table Comment",
                        width: 300, tooltipField: "TableComment", hide: true
                    },
                    {
                        field: "ColumnName", headerName: "Name", hide: true
                    },
                    {
                        field: "ColumnCode", headerName: "Code"
                    },
                    {
                        field: "ColumnComment", headerName: "Comment",
                        width: 300, tooltipField: "ColumnComment"
                    },
                    {
                        field: "ColumnDataType", headerName: "Data Type"
                    },
                    {
                        field: "ColumnLength", headerName: "Length",
                        width: 120
                    },
                    {
                        field: "ColumnPrimaryKey", headerName: "P",
                        width: 150,
                        cellRenderer: function (item) {
                            if (item.value) {
                                var vr = `<b title="Primary Key">🔑</b>`;
                                if (item.data.ColumnClusterObject) {
                                    vr += `<b class="ml-2" title="Cluster Object">📏</b>`;
                                }
                                return vr;
                            }
                        }
                    },
                    {
                        field: "ColumnMandatory", headerName: "M",
                        width: 120,
                        cellRenderer: function (item) {
                            return item.value ? `<b title="NOT NULL">✍</b>` : '';
                        }
                    },
                    {
                        field: "ColumnDefaultValue", headerName: "Default Value",
                        cellRenderer: function (item) {
                            return item.value;
                        }
                    }
                ],
                autoGroupColumnDef: {
                    menuTabs: ['generalMenuTab', 'columnsMenuTab'],
                    field: 'ColumnName',
                    width: 550,
                    resizable: true,
                    cellRendererParams: {
                        suppressCount: true,
                        suppressEnterExpand: true,
                        suppressDoubleClickExpand: true,
                        innerRenderer: function (item) {
                            var grow = pdm.grid.gridOptions.rowData.filter(x => x[item.node.field] == item.value);
                            var row = grow[0];
                            switch (item.node.field) {
                                case "ModelCode":
                                    {
                                        let subg = pdm.groupBy(grow, gi => gi.TableName);
                                        return `<b>${item.value}</b> (${subg.length}) <span class="badge bg-light text-success mx-2">${row.PowerDesignerTarget}</span> <span class="badge bg-light">v${row.PowerDesignerVersion}</span>`
                                    }
                                case "TableName":
                                    {
                                        let subg = pdm.groupBy(grow, gi => gi.ColumnCode);
                                        return `<b>${row.TableCode}</b> (${subg.length}) <span class="badge text-secondary">${row.TableComment || ""}</span>`
                                    }
                                default:
                                    return `<b>${item.value}</b>`
                            }
                        }
                    }
                },
                animateRows: true,
                rowSelection: 'multiple',
                groupSelectsChildren: true,
                rowData: data,
                enableRangeSelection: true,
                defaultColDef: ss.agg.defaultColDef,
                localeText: ss.agg.localeText,
                components: {

                },
                onGridReady: function () {
                    //表格创建完成后执行的事件
                    //gridOptions.api.sizeColumnsToFit();//调整表格大小自适应
                }
            };

            pdm.grid = new agGrid.Grid(pdm.gbox, gridOptions);

            setTimeout(function () {
                gridOptions.api.forEachNode(function (node) {
                    if (node.group && node.level == 0) {
                        node.setExpanded(true);
                    }
                });
            }, 200)

            pdm.resize();
        },

        openUrl: function (url) {
            if (url == null) {
                url = location.hash.substr(1);
            }
            if (url.length < 4) {
                return;
            }
            pdm.tip("Loading ...");

            fetch(url).then(x => x.text()).then(res => {
                pdm.dataArray = pdm.dataArray.concat(pdm.parse(res));
                pdm.view(pdm.gridData());

                pdm.tip();
            }).catch(e => {
                console.log(e);
                pdm.tip("Loading ERROR");
            })
        },

        groupBy: function (array, f) {
            var groups = {};
            array.forEach(o => {
                var key = f(o);
                groups[key] = groups[key] || [];
                groups[key].push(o);
            });
            return Object.keys(groups);
        },

        resize: function () {
            var h = $(window).height() - $(pdm.gbox).offset().top - 15;
            pdm.gbox.style.height = h + "px";
        },

        parse: function (txt) {
            var di = pdm.dataItem();
            di.xml = new DOMParser().parseFromString(di.txt = txt, "text/xml");
            di.json = {
                PowerDesigner: pdm.getPowerDesigner(di),
                Model: pdm.getModel(di),
                Users: pdm.getUsers(di),
                TargetModels: pdm.getTargetModels(di),
                Tables: pdm.getTables(di)
            };
            return di;
        },

        visualDate: function (u) {
            return new Date(u * 1000 + 8 * 3600 * 1000).toISOString().replace("T", " ").replace("Z", "");
        },

        getKeyObj: function (kp) {
            var i = 0, ko = pdm.dataArray;
            do {
                ko = ko[kp[i++]];
            } while (i < kp.length);
            return ko
        },

        getTagName: function (di, kk, dom) {
            var tn = dom ? dom : [di.xml], kks = kk.split(',');

            for (var i = 0; i < kks.length; i++) {
                var k = kks[i];
                if (tn && tn.length == null) {
                    tn = tn.getElementsByTagName(k);
                } else if (tn && tn.length) {
                    tn = tn[0].getElementsByTagName(k);
                }
            }

            return tn;
        },

        getNameValue: function (di, kk, dom) {
            var nd = pdm.getTagName(di, kk, dom);
            if (nd && nd[0]) {
                return nd[0].innerHTML;
            }
            return null;
        },

        getAttr: function (kk, dom) {
            if (dom) {
                return dom.getAttribute(kk);
            }
            return null;
        },

        getPowerDesigner: function (di) {
            var vt = di.txt.substr(di.txt.indexOf("<?PowerDesigner"), Math.min(5000, di.txt.indexOf("<o:RootObject")));
            var obj = {
                AppLocale: /AppLocale="(\w+)"/.exec(vt)[1],
                Name: /Name="([\w,\., ]+)"/.exec(vt)[1],
                Target: /Target="([\w,\., ]+)"/.exec(vt)[1],
                version: /version="([\d,\.]+)"/.exec(vt)[1]
            }

            return obj;
        },

        getModel: function (di) {
            var mo = pdm.getTagName(di, "o:RootObject,c:Children,o:Model");
            var obj = {
                Name: pdm.getNameValue(di, "a:Name", mo),
                Code: pdm.getNameValue(di, "a:Code", mo),
                CreationDate: pdm.visualDate(pdm.getNameValue(di, "a:CreationDate", mo)),
                Creator: pdm.getNameValue(di, "a:Creator", mo),
                ModificationDate: pdm.visualDate(pdm.getNameValue(di, "a:ModificationDate", mo)),
                Modifier: pdm.getNameValue(di, "a:Modifier", mo)
            }

            return obj;
        },

        getUsers: function (di) {
            var nn = pdm.getTagName(di, "o:RootObject,c:Children,o:Model,c:Users,o:User");
            var obj = {
                Name: pdm.getNameValue(di, "a:Name", nn),
                Code: pdm.getNameValue(di, "a:Code", nn),
                CreationDate: pdm.visualDate(pdm.getNameValue(di, "a:CreationDate", nn)),
                Creator: pdm.getNameValue(di, "a:Creator", nn),
                ModificationDate: pdm.visualDate(pdm.getNameValue(di, "a:ModificationDate", nn)),
                Modifier: pdm.getNameValue(di, "a:Modifier", nn),
                Stereotype: pdm.getNameValue(di, "a:Stereotype", nn)
            }

            return obj;
        },

        getTargetModels: function (di) {
            var nn = pdm.getTagName(di, "o:RootObject,c:Children,o:Model,c:TargetModels,o:TargetModel");
            var obj = {
                Name: pdm.getNameValue(di, "a:Name", nn),
                Code: pdm.getNameValue(di, "a:Code", nn),
                CreationDate: pdm.visualDate(pdm.getNameValue(di, "a:CreationDate", nn)),
                ModificationDate: pdm.visualDate(pdm.getNameValue(di, "a:ModificationDate", nn))
            }

            return obj;
        },

        getTables: function (di) {
            var nn = pdm.getTagName(di, "o:RootObject,c:Children,o:Model,c:Tables,o:Table"), tables = [];

            for (var i = 0; i < nn.length; i++) {
                var nt = nn[i], table = {
                    Id: pdm.getAttr("Id", nt),
                    Name: pdm.getNameValue(di, "a:Name", nt),
                    Code: pdm.getNameValue(di, "a:Code", nt),
                    CreationDate: pdm.visualDate(pdm.getNameValue(di, "a:CreationDate", nt)),
                    Creator: pdm.getNameValue(di, "a:Creator", nt),
                    ModificationDate: pdm.visualDate(pdm.getNameValue(di, "a:ModificationDate", nt)),
                    Modifier: pdm.getNameValue(di, "a:Modifier", nt),
                    Comment: pdm.getNameValue(di, "a:Comment", nt),
                    PhysicalOptions: pdm.getNameValue(di, "a:PhysicalOptions", nt),
                    Columns: [],
                    Keys: [],
                    Indexes: [],
                    //主键 Keys id
                    PrimaryKey: null,
                    //聚集索引 Index id
                    ClusterObject: null
                }

                //Columns
                var nc = pdm.getTagName(di, "c:Columns,o:Column", nt);
                for (var j = 0; j < nc.length; j++) {
                    var cj = nc[j], column = {
                        Id: pdm.getAttr("Id", cj),
                        Name: pdm.getNameValue(di, "a:Name", cj),
                        Code: pdm.getNameValue(di, "a:Code", cj),
                        CreationDate: pdm.visualDate(pdm.getNameValue(di, "a:CreationDate", cj)),
                        Creator: pdm.getNameValue(di, "a:Creator", cj),
                        ModificationDate: pdm.visualDate(pdm.getNameValue(di, "a:ModificationDate", cj)),
                        Modifier: pdm.getNameValue(di, "a:Modifier", cj),
                        Comment: pdm.getNameValue(di, "a:Comment", cj),
                        DefaultValue: pdm.getNameValue(di, "a:DefaultValue", cj),
                        DataType: pdm.getNameValue(di, "a:DataType", cj),
                        Length: pdm.getNameValue(di, "a:Length", cj),
                        FieldOrder: (j + 1),
                        PrimaryKey: null,
                        ClusterObject: null,
                        Mandatory: pdm.getNameValue(di, "a:Column.Mandatory", cj),
                        ExtendedAttributesText: pdm.getNameValue(di, "a:ExtendedAttributesText", cj),
                    }
                    table.Columns.push(column);
                }

                //Keys
                var nk = pdm.getTagName(di, "c:Keys,o:Key", nt);
                for (var j = 0; j < nk.length; j++) {
                    var kj = nk[j], key = {
                        Id: pdm.getAttr("Id", kj),
                        Code: pdm.getNameValue(di, "a:Code", kj),
                        CreationDate: pdm.visualDate(pdm.getNameValue(di, "a:CreationDate", kj)),
                        Creator: pdm.getNameValue(di, "a:Creator", kj),
                        ModificationDate: pdm.getNameValue(di, "a:ModificationDate", kj),
                        Modifier: pdm.getNameValue(di, "a:Modifier", kj),
                        PhysicalOptions: pdm.getNameValue(di, "a:PhysicalOptions", kj),
                        ConstraintName: pdm.getNameValue(di, "a:ConstraintName", kj),
                        //Columns id
                        Columns: []
                    };

                    //Columns id
                    var nkc = pdm.getTagName(di, "c:Key.Columns,o:Column", kj);
                    for (var m = 0; m < nkc.length; m++) {
                        var id = pdm.getAttr("Ref", nkc[m]);
                        key.Columns.push(id);
                    }

                    table.Keys.push(key);
                }

                //Indexes
                var ni = pdm.getTagName(di, "c:Indexes,o:Index", nt);
                for (var j = 0; j < ni.length; j++) {
                    var ij = ni[j], index = {
                        Id: pdm.getAttr("Id", ij),
                        Name: pdm.getNameValue(di, "a:Name", ij),
                        Code: pdm.getNameValue(di, "a:Code", ij),
                        CreationDate: pdm.visualDate(pdm.getNameValue(di, "a:CreationDate", ij)),
                        Creator: pdm.getNameValue(di, "a:Creator", ij),
                        ModificationDate: pdm.visualDate(pdm.getNameValue(di, "a:ModificationDate", ij)),
                        Modifier: pdm.getNameValue(di, "a:Modifier", ij),
                        PhysicalOptions: pdm.getNameValue(di, "a:PhysicalOptions", ij),
                        Unique: pdm.getNameValue(di, "a:Unique", ij),
                        //Columns id
                        Columns: []
                    };

                    //Columns id
                    var niic = pdm.getTagName(di, "c:IndexColumns,o:IndexColumn", ij);
                    for (var m = 0; m < niic.length; m++) {
                        var id = pdm.getAttr("Ref", pdm.getTagName(di, "c:Column,o:Column", niic[m])[0]);
                        index.Columns.push(id);
                    }

                    table.Indexes.push(index);
                }

                //主键
                var hasPk = pdm.getTagName(di, "c:PrimaryKey,o:Key", nt);
                if (hasPk.length) {
                    table.PrimaryKey = pdm.getAttr("Ref", hasPk[0]);
                    var colid = table.Keys.filter(x => x.Id == table.PrimaryKey)[0].Columns;
                    table.Columns.forEach(c => {
                        var pki = colid.indexOf(c.Id);
                        c.PrimaryKey = pki == -1 ? null : pki + 1;
                    });
                }

                //聚集索引
                var hasCo = pdm.getTagName(di, "c:ClusterObject,o:Index", nt);
                if (hasCo.length) {
                    table.ClusterObject = pdm.getAttr("Ref", hasCo[0]);
                    var colid = table.Indexes.filter(x => x.Id == table.ClusterObject)[0].Columns;
                    table.Columns.forEach(c => {
                        var pki = colid.indexOf(c.Id);
                        c.ClusterObject = pki == -1 ? null : pki + 1;
                    });
                }

                tables.push(table);
            }

            return tables;
        }
    }

    pdm.init();

</script>
        </div>
</body>
</html>
